use std::any::Any;
use std::borrow::Borrow;
use std::pin::Pin;
use std::task::{Context, Poll};

use actix_http::error::{ErrorBadRequest, ErrorUnauthorized};
use actix_service::{Service, Transform};
use actix_web::{dev::ServiceRequest, dev::ServiceResponse, Error, http, HttpRequest, HttpResponse};
use actix_web::middleware::errhandlers::ErrorHandlerResponse::Response;
use futures::future::{Either, ok, Ready};
use futures::Future;

pub struct Authorization;

// Middleware factory is `Transform` trait from actix-service crate
// `S` - type of the next service
// `B` - type of response's body
impl<S, B> Transform<S> for Authorization
    where
        S: Service<Request=ServiceRequest, Response=ServiceResponse<B>, Error=Error>,
        S::Future: 'static,
        B: 'static,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse<B>;
    type Error = Error;
    // 和上面的Transform是两码事
    type Transform = AuthorizationMiddleware<S>;
    type InitError = ();
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ok(AuthorizationMiddleware { service })
    }
}

/// 必须是pub，也不能是pub(super)之类的
pub struct AuthorizationMiddleware<S> {
    service: S,
}

impl<S, B> Service for AuthorizationMiddleware<S>
    where
        S: Service<Request=ServiceRequest, Response=ServiceResponse<B>, Error=Error>,
        S::Future: 'static,
        B: 'static,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = Pin<Box<dyn Future<Output=Result<Self::Response, Self::Error>>>>;

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        self.service.poll_ready(cx)
    }

    fn call(&mut self, req: ServiceRequest) -> Self::Future {
        // TODO 改成日志打印
        eprintln!("start authorization middleware: {}", req.path());
        if !req.headers().contains_key("Authorization") {
            return Box::pin(async move {
                Ok(req.into_response(
                    HttpResponse::Ok().body("sorry, your have no authorization to access this api").into_body()
                ))
            });
        }

        // execute next logic
        let fut = self.service.call(req);

        return Box::pin(async move {
            let res = fut.await?;

            eprintln!("end authorization");
            Ok(res)
        });
    }
}
